#include "PCIdentify.h"

#include "cpp-httplib.h"
#include <iostream>
#include <string>
#include <cstdio>
#include <memory>
#include <array>
#include <algorithm>

#ifdef _WIN32
#include <Windows.h>
#include <intrin.h>  // for __cpuid
#else
#include <unistd.h>
#include <sys/utsname.h>
#endif

#include "MD5.h"

#ifdef _WIN32
#define POPEN _popen
#define PCLOSE _pclose
#else
#define POPEN popen
#define PCLOSE pclose
#endif

// ִ�������в��������
std::string exec_command(const char* cmd) {
	std::array<char, 128> buffer;
	std::string result;
	std::unique_ptr<FILE, decltype(&PCLOSE)> pipe(POPEN(cmd, "r"), PCLOSE);
	if (!pipe) {
		throw std::runtime_error("popen() failed!");
	}
	while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
		result += buffer.data();
	}
	return result;
}

// ��ȡ CPU ID����ƽ̨��
std::string get_cpu_id() {
	std::string cpu_id;

#ifdef _WIN32
	int cpu_info[4] = { -1 };
	char buffer[17] = { 0 };
	__cpuid(cpu_info, 1);  // EAX=1: Processor Info
	sprintf_s(buffer, "%08X%08X", cpu_info[3], cpu_info[0]);
	cpu_id = buffer;
#else
	// Linux: �� /proc/cpuinfo ��ȡ CPU ��Ϣ
	std::string cpuinfo = exec_command("cat /proc/cpuinfo | grep 'model name' | head -n 1");
	if (!cpuinfo.empty()) {
		cpu_id = cpuinfo.substr(cpuinfo.find(":") + 2);  // ��ȡ�ͺ�
		cpu_id.erase(std::remove(cpu_id.begin(), cpu_id.end(), '\n'), cpu_id.end());
		cpu_id.erase(std::remove(cpu_id.begin(), cpu_id.end(), '\r'), cpu_id.end());
	}
#endif

	return cpu_id;
}

// ��ȡ�������кţ���ƽ̨��
std::string get_motherboard_id() {
	std::string mb_serial;

#ifdef _WIN32
	mb_serial = exec_command("wmic baseboard get serialnumber | more +1");
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\n'), mb_serial.end());
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\r'), mb_serial.end());
#else
	mb_serial = exec_command("sudo dmidecode -s baseboard-serial-number 2>/dev/null");
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\n'), mb_serial.end());
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\r'), mb_serial.end());
	if (mb_serial.empty() || mb_serial.find("Not Specified") != std::string::npos) {
		mb_serial = exec_command("sudo dmidecode -s system-serial-number 2>/dev/null");
	}
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\n'), mb_serial.end());
	mb_serial.erase(std::remove(mb_serial.begin(), mb_serial.end(), '\r'), mb_serial.end());
#endif

	return mb_serial;
}

// ��ȡӲ�����кţ���ƽ̨��
std::string get_disk_serial() {
	std::string disk_serial;

#ifdef _WIN32
	disk_serial = exec_command("wmic diskdrive get serialnumber | more +1");
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\n'), disk_serial.end());
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\r'), disk_serial.end());
#else
	disk_serial = exec_command("lsblk -o SERIAL -n -d 2>/dev/null | head -n 1");
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\n'), disk_serial.end());
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\r'), disk_serial.end());
	if (disk_serial.empty()) {
		disk_serial = exec_command("udevadm info --query=property --name=/dev/sda | grep ID_SERIAL_SHORT | cut -d'=' -f2");
	}
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\n'), disk_serial.end());
	disk_serial.erase(std::remove(disk_serial.begin(), disk_serial.end(), '\r'), disk_serial.end());
#endif

	return disk_serial;
}

// ��ȡ MAC ��ַ����ƽ̨��
std::string get_mac_address() {
	std::string mac;

#ifdef _WIN32
	mac = exec_command("getmac");
	//mac = mac.substr(0, 17);  // ��ȡ��һ�� MAC
	mac.erase(std::remove(mac.begin(), mac.end(), '\n'), mac.end());
	mac.erase(std::remove(mac.begin(), mac.end(), '\r'), mac.end());
#else
	mac = exec_command("ip link | grep 'link/ether' | head -n 1 | awk '{print $2}'");
	mac.erase(std::remove(mac.begin(), mac.end(), '\n'), mac.end());
	mac.erase(std::remove(mac.begin(), mac.end(), '\r'), mac.end());
#endif

	return mac;
}

// ������� ID ����Ψһ��ʶ
std::string get_machine_id() {
	std::string cpu_id = get_cpu_id();
	std::string mb_serial = get_motherboard_id();
	std::string disk_serial = get_disk_serial();
	std::string mac = get_mac_address();
	return md5(cpu_id + mb_serial + disk_serial + mac);
}

bool Authorization()
{
	httplib::Client cli("x");

	auto res = cli.Post("/", "", "application/json");
	if (res)
	{
		std::cout << "Authorization Success\n" << res->body << std::endl;
		return true;
	}
	else
	{
		std::cout << "Authorization Failed" << std::endl;
		return false;
	}
}

